home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / cvs-1_3.lha / cvs-1.3 / contrib / pcl-cvs / cookie.el < prev    next >
Lisp/Scheme  |  1992-04-07  |  25KB  |  885 lines

  1. ;;; cookie.el,v 1.2 1992/04/07 20:49:12 berliner Exp
  2. ;;; cookie.el -- Utility to display cookies in buffers
  3. ;;; Copyright (C) 1991, 1992  Per Cederqvist
  4. ;;;
  5. ;;; This program is free software; you can redistribute it and/or modify
  6. ;;; it under the terms of the GNU General Public License as published by
  7. ;;; the Free Software Foundation; either version 2 of the License, or
  8. ;;; (at your option) any later version.
  9. ;;;
  10. ;;; This program is distributed in the hope that it will be useful,
  11. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. ;;; GNU General Public License for more details.
  14. ;;;
  15. ;;; You should have received a copy of the GNU General Public License
  16. ;;; along with this program; if not, write to the Free Software
  17. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19. ;;;; TO-DO: Byt namn! tin -> wrapper (eller n}got b{ttre).
  20.  
  21. ;;; Note that this file is still under development.  Comments,
  22. ;;; enhancements and bug fixes are welcome.
  23. ;;; Send them to ceder@lysator.liu.se.
  24.  
  25. (defun impl nil (error "Not yet implemented!"))
  26.  
  27. ;;; Cookie is a package that imlements a connection between an
  28. ;;; elib-dll and the contents of a buffer. Possible uses are dired
  29. ;;; (have all files in a list, and show them), buffer-list,
  30. ;;; kom-prioritize (in the LysKOM elisp client) and others. pcl-cvs.el
  31. ;;; uses cookie.el.
  32. ;;;
  33. ;;; A cookie buffer contains a header, any number of cookies, and a
  34. ;;; footer. The header and footer are constant strings that are given
  35. ;;; to cookie-create when the buffer is placed under cookie. Each cookie
  36. ;;; is displayed in the buffer by calling a user-supplied function
  37. ;;; that takes a cookie and returns a string. The string may be
  38. ;;; empty, or contain any number of lines. An extra newline is always
  39. ;;; appended unless the string is empty.
  40. ;;;
  41. ;;; Cookie does not affect the mode of the buffer in any way. It
  42. ;;; merely makes it easy to connect an underlying data representation
  43. ;;; to the buffer contents.
  44. ;;;
  45. ;;; The cookie-node data type:
  46. ;;;      start-marker
  47. ;;;      ;; end-marker      This field is no longer present.
  48. ;;;      cookie          The user-supplied element.
  49. ;;;
  50. ;;; A dll of cookie-nodes are held in the buffer local variable
  51. ;;; cake-tin.
  52. ;;;
  53. ;;; A tin is an object that contains one cookie. You can get the next
  54. ;;; and previous tin.
  55. ;;;
  56.  
  57. (require 'elib-dll)
  58. (provide 'cookie)
  59.  
  60. (defvar cookies nil
  61.   "A doubly linked list that contains the underlying data representation
  62. for the contents of a cookie buffer. The package elib-dll is used to
  63. manipulate this list.")
  64.  
  65. (defvar cookie-pretty-printer nil
  66.   "The function that is used to pretty-print a cookie in this buffer.")
  67.  
  68. (defvar cookie-header nil
  69.   "The tin that holds the header cookie.")
  70.  
  71. (defvar cookie-footer nil
  72.   "The tin that holds the footer cookie.")
  73.  
  74. (defvar cookie-last-tin nil
  75.   "The tin the cursor was positioned at, the last time the cookie
  76. package checked the cursor position. Buffer local in all buffers
  77. the cookie package works on. You may set this if your package
  78. thinks it knows where the cursor will be the next time this
  79. package is called. It can speed things up.
  80.  
  81. It must never be set to a tin that has been deleted.")
  82.  
  83. ;;; ================================================================
  84. ;;;      Internal functions for use in the cookie package
  85.  
  86. (put 'cookie-set-buffer 'lisp-indent-hook 1)
  87.  
  88. (defmacro cookie-set-buffer (buffer &rest forms)
  89.  
  90.   ;; Execute FORMS with BUFFER selected as current buffer.
  91.   ;; Return value of last form in FORMS.  INTERNAL USE ONLY.
  92.  
  93.   (let ((old-buffer (make-symbol "old-buffer")))
  94.     (` (let (((, old-buffer) (current-buffer)))
  95.      (set-buffer (get-buffer-create (, buffer)))
  96.      (unwind-protect
  97.          (progn (,@ forms))
  98.        (set-buffer (, old-buffer)))))))
  99.  
  100.  
  101. (defmacro cookie-filter-hf (tin)
  102.  
  103.   ;; Evaluate TIN once and return it. BUT if it is
  104.   ;; equal to cookie-header or cookie-footer return nil instead.
  105.   ;; INTERNAL USE ONLY.
  106.  
  107.   (let ((tempvar (make-symbol "tin")))
  108.     (` (let (((, tempvar) (, tin)))
  109.      (if (or (eq (, tempvar) cookie-header)
  110.          (eq (, tempvar) cookie-footer))
  111.          nil
  112.        (, tempvar))))))
  113.  
  114.  
  115. ;;; cookie-tin
  116. ;;; Constructor:
  117.  
  118. (defun cookie-create-tin (start-marker
  119.               cookie)
  120.   ;; Create a  tin.   INTERNAL USE ONLY.
  121.   (cons 'COOKIE-TIN (vector start-marker nil cookie)))
  122.  
  123.  
  124. ;;; Selectors:
  125.  
  126. (defun cookie-tin-start-marker (cookie-tin)
  127.   ;; Get start-marker from cookie-tin.    INTERNAL USE ONLY.
  128.   (elt (cdr cookie-tin) 0))
  129.  
  130. ;(defun cookie-tin-end-marker (cookie-tin)
  131. ;  ;;Get end-marker from cookie-tin.   INTERNAL USE ONLY.
  132. ;  (elt (cdr cookie-tin) 1))
  133.  
  134. (defun cookie-tin-cookie-safe (cookie-tin)
  135.   ;; Get cookie from cookie-tin.   INTERNAL USE ONLY.
  136.   ;; Returns nil if given nil as input.
  137.   ;; This is the same as cookie-tin-cookie in version 18.57
  138.   ;; of emacs, but elt should signal an error when given nil
  139.   ;; as input (according to the info files).
  140.   (elt (cdr cookie-tin) 2))
  141.  
  142. (defun cookie-tin-cookie (cookie-tin)
  143.   ;; Get cookie from cookie-tin.   INTERNAL USE ONLY.
  144.   (elt (cdr cookie-tin) 2))
  145.  
  146.  
  147. ;;; Modifiers:
  148.  
  149. (defun set-cookie-tin-start-marker (cookie-tin newval)
  150.   ;; Set start-marker in cookie-tin to NEWVAL.   INTERNAL USE ONLY.
  151.   (aset (cdr cookie-tin) 0 newval))
  152.  
  153. ;(defun set-cookie-tin-end-marker (cookie-tin newval)
  154. ;  ;; Set end-marker in cookie-tin to NEWVAL.   INTERNAL USE ONLY.
  155. ;  (aset (cdr cookie-tin) 1 newval))
  156.  
  157. (defun set-cookie-tin-cookie (cookie-tin newval)
  158.   ;; Set cookie in cookie-tin to NEWVAL.   INTERNAL USE ONLY.
  159.   (aset (cdr cookie-tin) 2 newval))
  160.  
  161.  
  162.  
  163. ;;; Predicate:
  164.  
  165. (defun cookie-tin-p (object)
  166.   ;; Return t if OBJECT is a tin.   INTERNAL USE ONLY.
  167.   (eq (car-safe object) 'COOKIE-TIN))
  168.  
  169. ;;; end of cookie-tin data type.
  170.                  
  171.  
  172. (defun cookie-create-tin-and-insert (cookie string pos)
  173.   ;; Insert STRING at POS in current buffer. Remember start
  174.   ;; position. Create a tin containing them and the COOKIE.
  175.   ;;    INTERNAL USE ONLY.
  176.  
  177.   (save-excursion
  178.     (goto-char pos)
  179.     ;; Remember the position as a number so that it doesn't move
  180.     ;; when we insert the string.
  181.     (let ((start (if (markerp pos)
  182.              (marker-position pos)
  183.            pos)))
  184.       ;; Use insert-before-markers so that the marker for the
  185.       ;; next cookie is updated.
  186.       (insert-before-markers string)
  187.       (insert-before-markers ?\n)
  188.       (cookie-create-tin (copy-marker start) cookie))))
  189.  
  190.  
  191. (defun cookie-delete-tin-internal (tin)
  192.   ;; Delete a cookie from the buffer.  INTERNAL USE ONLY.
  193.   ;; Can not be used on the footer.
  194.   (delete-region (cookie-tin-start-marker (dll-element cookies tin))
  195.          (cookie-tin-start-marker
  196.           (dll-element cookies
  197.                    (dll-next cookies  tin)))))
  198.  
  199.  
  200.  
  201. (defun cookie-refresh-tin (tin)
  202.   ;; Redisplay the cookie represented by TIN. INTERNAL USE ONLY.
  203.   ;; Can not be used on the footer.
  204.  
  205.   (save-excursion
  206.     ;; First, remove the string:
  207.     (delete-region (cookie-tin-start-marker (dll-element cookies tin))
  208.            (1- (marker-position
  209.             (cookie-tin-start-marker
  210.              (dll-element cookies
  211.                       (dll-next cookies  tin))))))
  212.  
  213.     ;; Calculate and insert the string.
  214.  
  215.     (goto-char (cookie-tin-start-marker (dll-element cookies tin)))
  216.     (insert
  217.      (funcall cookie-pretty-printer
  218.           (cookie-tin-cookie (dll-element cookies tin))))))
  219.  
  220.  
  221. ;;; ================================================================
  222. ;;;      The public members of the cookie package
  223.  
  224.  
  225. (defun cookie-cookie (buffer tin)
  226.   "Get the cookie from a TIN. Args: BUFFER TIN."
  227.   (cookie-set-buffer buffer
  228.     (cookie-tin-cookie (dll-element cookies tin))))
  229.  
  230.  
  231.  
  232.  
  233. (defun cookie-create (buffer pretty-printer &optional header footer)
  234.  
  235.   "Start to use the cookie package in BUFFER.
  236. BUFFER may be a buffer or a buffer name. It is created if it does not exist.
  237. Beware that the entire contents of the buffer will be erased.
  238. PRETTY-PRINTER is a function that takes one cookie and returns a string
  239. to be displayed in the buffer. The string may be empty. If it is not
  240. empty a newline will be added automatically. It may span several lines.
  241. Optional third argument HEADER is a string that will always be present
  242. at the top of the buffer. HEADER should end with a newline. Optionaly
  243. fourth argument FOOTER is similar, and will always be inserted at the
  244. bottom of the buffer."
  245.  
  246.   (cookie-set-buffer buffer
  247.  
  248.     (erase-buffer)
  249.  
  250.     (make-local-variable 'cookie-last-tin)
  251.     (make-local-variable 'cookie-pretty-printer)
  252.     (make-local-variable 'cookie-header)
  253.     (make-local-variable 'cookie-footer)
  254.     (make-local-variable 'cookies)
  255.  
  256.     (setq cookie-last-tin nil)
  257.     (setq cookie-pretty-printer pretty-printer)
  258.     (setq cookies (dll-create))
  259.  
  260.     (dll-enter-first cookies
  261.              (cookie-create-tin-and-insert
  262.               header header 0))
  263.     (setq cookie-header (dll-nth cookies 0))
  264.  
  265.     (dll-enter-last cookies
  266.             (cookie-create-tin-and-insert
  267.              footer footer (point-max)))
  268.     (setq cookie-footer (dll-nth cookies -1))
  269.  
  270.     (goto-char (point-min))
  271.     (forward-line 1)))
  272.  
  273.  
  274. (defun cookie-set-header (buffer header)
  275.   "Change the header. Args: BUFFER HEADER."
  276.   (impl))
  277.  
  278.  
  279. (defun cookie-set-footer (buffer header)
  280.   "Change the footer. Args: BUFFER FOOTER."
  281.   (impl))
  282.  
  283.  
  284.  
  285. (defun cookie-enter-first (buffer cookie)
  286.   "Enter a COOKIE first in BUFFER.
  287. Args: BUFFER COOKIE."
  288.  
  289.   (cookie-set-buffer buffer
  290.  
  291.     ;; It is always safe to insert an element after the first element,
  292.     ;; because the header is always present. (dll-nth cookies 0) should
  293.     ;; never return nil.
  294.  
  295.     (dll-enter-after
  296.      cookies
  297.      (dll-nth cookies 0)
  298.      (cookie-create-tin-and-insert
  299.       cookie
  300.       (funcall cookie-pretty-printer cookie)
  301.       (cookie-tin-start-marker
  302.        (dll-element cookies (dll-nth cookies 1)))))))
  303.  
  304.  
  305.  
  306. (defun cookie-enter-last (buffer cookie)
  307.   "Enter a COOKIE last in BUFFER.
  308. Args: BUFFER COOKIE."
  309.  
  310.   (cookie-set-buffer buffer
  311.  
  312.     ;; Remember that the header and footer are always present. There
  313.     ;; is no need to check if (dll-nth cookies -2) returns nil.
  314.  
  315.     (dll-enter-before
  316.      cookies
  317.      (dll-nth cookies -1)
  318.      (cookie-create-tin-and-insert
  319.       cookie
  320.       (funcall cookie-pretty-printer cookie)
  321.       (cookie-tin-start-marker (dll-last cookies))))))
  322.  
  323.  
  324. (defun cookie-enter-after (buffer node cookie)
  325.   (impl))
  326.  
  327.  
  328. (defun cookie-enter-before (buffer node cookie)
  329.   (impl))
  330.  
  331.  
  332.  
  333. (defun cookie-next (buffer tin)
  334.   "Get the next tin. Args: BUFFER TIN.
  335. Returns nil if TIN is nil or the last cookie."
  336.   (if tin
  337.       (cookie-set-buffer buffer
  338.     (cookie-filter-hf (dll-next cookies tin)))))
  339.  
  340.  
  341.  
  342. (defun cookie-previous (buffer tin)
  343.   "Get the previous tin. Args: BUFFER TIN.
  344. Returns nil if TIN is nil or the first cookie."
  345.   (if tin
  346.       (cookie-set-buffer buffer
  347.     (cookie-filter-hf (dll-previous cookies tin)))))
  348.  
  349.  
  350. (defun cookie-nth (buffer n)
  351.  
  352.   "Return the Nth tin. Args: BUFFER N.
  353. N counts from zero. Nil is returned if there is less than N cookies.
  354. If N is negative, return the -(N+1)th last element.
  355. Thus, (cookie-nth dll 0) returns the first node,
  356. and (cookie-nth dll -1) returns the last node.
  357.  
  358. Use cookie-cookie to extract the cookie from the tin."
  359.  
  360.   (cookie-set-buffer buffer
  361.  
  362.     ;; Skip the header (or footer, if n is negative).
  363.     (if (< n 0)
  364.     (setq n (1- n))
  365.       (setq n (1+ n)))
  366.  
  367.     (cookie-filter-hf (dll-nth cookies n))))
  368.  
  369.  
  370.  
  371. (defun cookie-delete (buffer tin)
  372.   "Delete a cookie. Args: BUFFER TIN."
  373.  
  374.   (cookie-set-buffer buffer
  375.     (if (eq cookie-last-tin tin)
  376.     (setq cookie-last-tin nil))
  377.  
  378.     (cookie-delete-tin-internal tin)
  379.     (dll-delete cookies tin)))
  380.  
  381.  
  382.  
  383. (defun cookie-delete-first (buffer)
  384.   "Delete first cookie and return it. Args: BUFFER.
  385. Returns nil if there is no cookie left."
  386.  
  387.   (cookie-set-buffer buffer
  388.  
  389.     ;; We have to check that we do not try to delete the footer.
  390.  
  391.     (let ((tin (dll-nth cookies 1)))    ;Skip the header.
  392.       (if (eq tin cookie-footer)
  393.       nil
  394.     (cookie-delete-tin-internal tin)
  395.     (cookie-tin-cookie (dll-delete cookies tin))))))
  396.  
  397.  
  398.  
  399. (defun cookie-delete-last (buffer)
  400.   "Delete last cookie and return it. Args: BUFFER.
  401. Returns nil if there is no cookie left."
  402.  
  403.   (cookie-set-buffer buffer
  404.  
  405.     ;; We have to check that we do not try to delete the header.
  406.  
  407.     (let ((tin (dll-nth cookies -2)))    ;Skip the footer.
  408.       (if (eq tin cookie-header)
  409.       nil
  410.     (cookie-delete-tin-internal tin)
  411.     (cookie-tin-cookie (dll-delete cookies tin))))))
  412.  
  413.  
  414.  
  415. (defun cookie-first (buffer)
  416.  
  417.   "Return the first cookie in BUFFER. The cookie is not removed."
  418.  
  419.   (cookie-set-buffer buffer
  420.     (let ((tin (cookie-filter-hf (dll-nth cookies -1))))
  421.       (if tin
  422.       (cookie-tin-cookie-safe
  423.        (dll-element cookies tin))))))
  424.  
  425.  
  426. (defun cookie-last (buffer)
  427.  
  428.   "Return the last cookie in BUFFER. The cookie is not removed."
  429.  
  430.   (cookie-set-buffer buffer
  431.     (let ((tin (cookie-filter-hf (dll-nth cookies -2))))
  432.       (if tin
  433.       (cookie-tin-cookie-safe
  434.        (dll-element cookies tin))))))
  435.  
  436.  
  437. (defun cookie-empty (buffer)
  438.  
  439.   "Return true if there are no cookies in BUFFER."
  440.  
  441.   (cookie-set-buffer buffer
  442.     (eq (dll-nth cookies 1) cookie-footer)))
  443.  
  444.  
  445. (defun cookie-length (buffer)
  446.  
  447.   "Return number of cookies in BUFFER."
  448.  
  449.   ;; Don't count the footer and header.
  450.  
  451.   (cookie-set-buffer buffer
  452.     (- (dll-length cookies) 2)))
  453.  
  454.  
  455. (defun cookie-all (buffer)
  456.  
  457.   "Return a list of all cookies in BUFFER."
  458.  
  459.   (cookie-set-buffer buffer
  460.     (let (result 
  461.       (tin (dll-nth cookies -2)))
  462.       (while (not (eq tin cookie-header))
  463.     (setq result (cons (cookie-tin-cookie (dll-element cookies tin))
  464.                result))
  465.     (setq tin (dll-previous cookies tin)))
  466.       result)))
  467.  
  468. (defun cookie-clear (buffer)
  469.  
  470.   "Remove all cookies in buffer."
  471.  
  472.   (cookie-set-buffer buffer
  473.     (cookie-create buffer cookie-pretty-printer
  474.            (cookie-tin-cookie (dll-element cookies cookie-header))
  475.            (cookie-tin-cookie (dll-element cookies cookie-footer)))))
  476.  
  477.  
  478.  
  479. (defun cookie-map (map-function buffer &rest map-args)
  480.  
  481.   "Apply MAP-FUNCTION to all cookies in BUFFER.
  482. MAP-FUNCTION is applied to the first element first.
  483. If MAP-FUNCTION returns non-nil the cookie will be refreshed.
  484.  
  485. Note that BUFFER will be current buffer when MAP-FUNCTION is called.
  486.  
  487. If more than two arguments are given to cookie-map, remaining
  488. arguments will be passed to MAP-FUNCTION."
  489.  
  490.   (cookie-set-buffer buffer
  491.     (let ((tin (dll-nth cookies 1))
  492.       result)
  493.  
  494.       (while (not (eq tin cookie-footer))
  495.  
  496.     (if (apply map-function
  497.            (cookie-tin-cookie (dll-element cookies tin))
  498.            map-args)
  499.         (cookie-refresh-tin tin))
  500.  
  501.     (setq tin (dll-next cookies tin))))))
  502.  
  503.  
  504.  
  505. (defun cookie-map-reverse (map-function buffer &rest map-args)
  506.  
  507.   "Apply MAP-FUNCTION to all cookies in BUFFER.
  508. MAP-FUNCTION is applied to the last cookie first.
  509. If MAP-FUNCTION returns non-nil the cookie will be refreshed.
  510.  
  511. Note that BUFFER will be current buffer when MAP-FUNCTION is called.
  512.  
  513. If more than two arguments are given to cookie-map, remaining
  514. arguments will be passed to MAP-FUNCTION."
  515.  
  516.   (cookie-set-buffer buffer
  517.     (let ((tin (dll-nth cookies -2))
  518.       result)
  519.  
  520.       (while (not (eq tin cookie-header))
  521.  
  522.     (if (apply map-function
  523.            (cookie-tin-cookie (dll-element cookies tin))
  524.            map-args)
  525.         (cookie-refresh-tin tin))
  526.  
  527.     (setq tin (dll-previous cookies tin))))))
  528.  
  529.  
  530.  
  531. (defun cookie-enter-cookies (buffer cookie-list)
  532.  
  533.   "Insert all cookies in the list COOKIE-LIST last in BUFFER.
  534. Args: BUFFER COOKIE-LIST."
  535.  
  536.   (while cookie-list
  537.     (cookie-enter-last buffer (car cookie-list))
  538.     (setq cookie-list (cdr cookie-list))))
  539.  
  540.  
  541. (defun cookie-filter (buffer predicate)
  542.  
  543.   "Remove all cookies in BUFFER for which PREDICATE returns nil.
  544. Note that BUFFER will be current-buffer when PREDICATE is called.
  545.  
  546. The PREDICATE is called with one argument, the cookie."
  547.  
  548.   (cookie-set-buffer buffer
  549.     (let ((tin (dll-nth cookies 1))
  550.       next)
  551.       (while (not (eq tin cookie-footer))
  552.     (setq next (dll-next cookies tin))
  553.     (if (funcall predicate (cookie-tin-cookie (dll-element cookies tin)))
  554.         nil
  555.       (cookie-delete-tin-internal tin)
  556.       (dll-delete cookies tin))
  557.     (setq tin next)))))
  558.  
  559.  
  560. (defun cookie-filter-tins (buffer predicate)
  561.  
  562.   "Remove all cookies in BUFFER for which PREDICATE returns nil.
  563. Note that BUFFER will be current-buffer when PREDICATE is called.
  564.  
  565. The PREDICATE is called with one argument, the tin."
  566.  
  567.   (cookie-set-buffer buffer
  568.     (let ((tin (dll-nth cookies 1))
  569.       next)
  570.       (while (not (eq tin cookie-footer))
  571.     (setq next (dll-next cookies tin))
  572.     (if (funcall predicate tin)
  573.         nil
  574.       (cookie-delete-tin-internal tin)
  575.       (dll-delete cookies tin))
  576.     (setq tin next)))))
  577.  
  578. (defun cookie-pos-before-middle-p (pos tin1 tin2)
  579.  
  580.   "Return true if POS is in the first half of the region defined by TIN1 and
  581. TIN2."
  582.  
  583.   (< pos (/ (+ (cookie-tin-start-marker (dll-element cookeis tin1))
  584.            (cookie-tin-start-marker (dll-element cookeis tin2)))
  585.         2)))
  586.   
  587.  
  588. (defun cookie-get-selection (buffer pos &optional guess force-guess)
  589.  
  590.   "Return the tin the POS is within.
  591. Args: BUFFER POS &optional GUESS FORCE-GUESS.
  592. GUESS should be a tin that it is likely that POS is near. If FORCE-GUESS
  593. is non-nil GUESS is always used as a first guess, otherwise the first
  594. guess is the first tin, last tin, or GUESS, whichever is nearest to
  595. pos in the BUFFER.
  596.  
  597. If pos points within the header, the first cookie is returned.
  598. If pos points within the footer, the last cookie is returned.
  599. Nil is returned if there is no cookie.
  600.  
  601. It is often good to specify cookie-last-tin as GUESS, but remember
  602. that cookie-last-tin is buffer local in all buffers that cookie
  603. operates on."
  604.  
  605.   (cookie-set-buffer buffer
  606.  
  607.     (cond
  608.      ; No cookies present?
  609.      ((eq (dll-nth cookies 1) (dll-nth cookies -1))
  610.       nil)
  611.  
  612.      ; Before first cookie?
  613.      ((< pos (cookie-tin-start-marker
  614.           (dll-element cookies (dll-nth cookies 1))))
  615.       (dll-nth cookies 1))
  616.  
  617.      ; After last cookie?
  618.      ((>= pos (cookie-tin-start-marker (dll-last cookies)))
  619.       (dll-nth cookies -2))
  620.  
  621.      ; We now now that pos is within a cookie.
  622.      (t
  623.       ; Make an educated guess about which of the three known
  624.       ; cookies (the first, the last, or GUESS) is nearest.
  625.       (setq
  626.        guess
  627.        (cond
  628.     (force-guess guess)
  629.     (guess
  630.      (cond
  631.       ;; Closest to first cookie?
  632.       ((cookie-pos-before-middle-p
  633.         pos guess
  634.         (dll-nth cookies 1))
  635.        (dll-nth cookies 1))
  636.       ;; Closest to GUESS?
  637.       ((cookie-pos-before-middle-p
  638.         pos guess
  639.         cookie-footer)
  640.        guess)
  641.       ;; Closest to last cookie.
  642.       (t (dll-previous cookies cookie-footer))))
  643.     (t
  644.      ;; No guess given.
  645.      (cond
  646.       ;; First half?
  647.       ((cookie-pos-before-middle-p
  648.         pos (dll-nth cookies 1)
  649.         cookie-footer)    
  650.        (dll-nth cookies 1))
  651.       (t (dll-previous cookies cookie-footer))))))
  652.  
  653.       ;; GUESS is now a "best guess".
  654.      
  655.       ;; Find the correct cookie. First determine in which direction
  656.       ;; it lies, and then move in that direction until it is found.
  657.     
  658.       (cond
  659.        ;; Is pos after the guess?
  660.        ((>= pos (cookie-tin-start-marker (dll-element cookiess guess)))
  661.  
  662.     ;; Loop until we are exactly one cookie too far down...
  663.     (while (>= pos (cookie-tin-start-marker (dll-element cookiess guess)))
  664.       (setq guess (dll-next cookies guess)))
  665.  
  666.     ;; ...and return the previous cookie.
  667.     (dll-previous cookies guess))
  668.  
  669.        ;; Pos is before guess
  670.        (t
  671.  
  672.     (while (< pos (cookie-tin-start-marker (dll-element cookiess guess)))
  673.       (setq guess (dll-previous cookies guess)))
  674.  
  675.     guess))))))
  676.  
  677.  
  678. (defun cookie-start-marker (buffer tin)
  679.  
  680.   "Return start-position of a cookie in BUFFER.
  681. Args: BUFFER TIN.
  682. The marker that is returned should not be modified in any way,
  683. and is only valid until the contents of the cookie buffer changes."
  684.  
  685.   (cookie-set-buffer buffer
  686.     (cookie-tin-start-marker (dll-element cookies tin))))
  687.  
  688.  
  689. (defun cookie-end-marker (buffer tin)
  690.  
  691.   "Return end-position of a cookie in BUFFER.
  692. Args: BUFFER TIN.
  693. The marker that is returned should not be modified in any way,
  694. and is only valid until the contents of the cookie buffer changes."
  695.  
  696.   (cookie-set-buffer buffer
  697.     (cookie-tin-start-marker
  698.      (dll-element cookies (dll-next cookies tin)))))
  699.  
  700.  
  701.  
  702. (defun cookie-refresh (buffer)
  703.  
  704.   "Refresh all cookies in BUFFER.
  705. Cookie-pretty-printer will be called for all cookies and the new result
  706. displayed.
  707.  
  708. See also cookie-invalidate-tins."
  709.  
  710.   (cookie-set-buffer buffer
  711.  
  712.     (erase-buffer)
  713.  
  714.     (set-marker (cookie-tin-start-marker (dll-element cookies cookie-header))
  715.         (point) buffer)
  716.     (insert (cookie-tin-cookie (dll-element cookies cookie-header)))
  717.     (insert "\n")
  718.     
  719.     (let ((tin (dll-nth cookies 1)))
  720.       (while (not (eq tin cookie-footer))
  721.  
  722.     (set-marker (cookie-tin-start-marker (dll-element cookies tin))
  723.             (point) buffer)
  724.     (insert
  725.      (funcall cookie-pretty-printer
  726.           (cookie-tin-cookie (dll-element cookies tin))))
  727.     (insert "\n")
  728.     (setq tin (dll-next cookies tin))))
  729.     
  730.     (set-marker (cookie-tin-start-marker (dll-element cookies cookie-footer))
  731.         (point) buffer)
  732.     (insert (cookie-tin-cookie (dll-element cookies cookie-footer)))
  733.     (insert "\n")))
  734.  
  735.  
  736. (defun cookie-invalidate-tins (buffer &rest tins)
  737.  
  738.   "Refresh some cookies.
  739. Args: BUFFER &rest TINS."
  740.  
  741.   (cookie-set-buffer buffer
  742.     
  743.     (while tins
  744.       (cookie-refresh-tin (car tins))
  745.       (setq tins (cdr tins)))))
  746.  
  747.  
  748. ;;; Cookie movement commands.
  749.  
  750. (defun cookie-set-goal-column (buffer goal)
  751.   "Set goal-column for BUFFER.
  752. Args: BUFFER GOAL.
  753. goal-column is made buffer-local."
  754.   (cookie-set-buffer buffer
  755.     (make-local-variable 'goal-column)
  756.     (setq goal-column goal)))
  757.  
  758.  
  759. (defun cookie-previous-cookie (buffer pos arg)
  760.   "Move point to the ARGth previous cookie.
  761. Don't move if we are at the first cookie.
  762. ARG is the prefix argument when called interactively.
  763. Args: BUFFER POS ARG.
  764. Sets cookie-last-tin to the cookie we move to."
  765.  
  766.   (interactive (list (current-buffer) (point)
  767.              (prefix-numeric-value current-prefix-arg)))
  768.  
  769.   (cookie-set-buffer buffer
  770.     (setq cookie-last-tin
  771.       (cookie-get-selection buffer pos cookie-last-tin))
  772.  
  773.     (while (and cookie-last-tin (> arg 0))
  774.       (setq arg (1- arg))
  775.       (setq cookie-last-tin 
  776.         (dll-previous cookies cookie-last-tin)))
  777.  
  778.     ;; Never step above the first cookie.
  779.  
  780.     (if (null (cookie-filter-hf cookie-last-tin))
  781.     (setq cookie-last-tin (dll-nth cookies 1)))
  782.  
  783.     (goto-char
  784.      (cookie-tin-start-marker
  785.       (dll-element cookies cookie-last-tin)))
  786.  
  787.     (if goal-column
  788.     (move-to-column goal-column))))
  789.  
  790.  
  791.  
  792. (defun cookie-next-cookie (buffer pos arg)
  793.   "Move point to the ARGth next cookie.
  794. Don't move if we are at the last cookie.
  795. ARG is the prefix argument when called interactively.
  796. Args: BUFFER POS ARG.
  797. Sets cookie-last-tin to the cookie we move to."
  798.  
  799.   (interactive (list (current-buffer) (point)
  800.              (prefix-numeric-value current-prefix-arg)))
  801.  
  802.   (cookie-set-buffer buffer
  803.     (setq cookie-last-tin
  804.       (cookie-get-selection buffer pos cookie-last-tin))
  805.  
  806.     (while (and cookie-last-tin (> arg 0))
  807.       (setq arg (1- arg))
  808.       (setq cookie-last-tin 
  809.         (dll-next cookies cookie-last-tin)))
  810.  
  811.     (if (null (cookie-filter-hf cookie-last-tin))
  812.     (setq cookie-last-tin (dll-nth cookies -2)))
  813.  
  814.     (goto-char
  815.      (cookie-tin-start-marker
  816.       (dll-element cookies cookie-last-tin)))
  817.  
  818.     (if goal-column
  819.     (move-to-column goal-column))))
  820.  
  821.  
  822. (defun cookie-collect-tins (buffer predicate &rest predicate-args)
  823.  
  824.   "Return a list of all tins in BUFFER whose cookie PREDICATE
  825. returns true for.
  826. PREDICATE is a function that takes a cookie as its argument.
  827. The tins on the returned list will appear in the same order
  828. as in the buffer. You should not rely on in which order PREDICATE
  829. is called. Note that BUFFER is current-buffer when PREDICATE
  830. is called. (If you call cookie-collect with another buffer set
  831. as current-buffer and need to access buffer-local variables
  832. from that buffer within PREDICATE you must send them via
  833. PREDICATE-ARGS).
  834.  
  835. If more than two arguments are given to cookie-collect the remaining
  836. arguments will be passed to PREDICATE.
  837.  
  838. Use cookie-cookie to get the cookie from the tin."
  839.  
  840.   (cookie-set-buffer buffer
  841.     (let ((tin (dll-nth cookies -2))
  842.       result)
  843.  
  844.       (while (not (eq tin cookie-header))
  845.  
  846.     (if (apply predicate
  847.            (cookie-tin-cookie (dll-element cookies tin))
  848.            predicate-args)
  849.         (setq result (cons tin result)))
  850.  
  851.     (setq tin (dll-previous cookies tin)))
  852.       result)))
  853.  
  854.  
  855. (defun cookie-collect-cookies (buffer predicate &rest predicate-args)
  856.  
  857.   "Return a list of all cookies in BUFFER that PREDICATE
  858. returns true for.
  859. PREDICATE is a function that takes a cookie as its argument.
  860. The cookie on the returned list will appear in the same order
  861. as in the buffer. You should not rely on in which order PREDICATE
  862. is called. Note that BUFFER is current-buffer when PREDICATE
  863. is called. (If you call cookie-collect with another buffer set
  864. as current-buffer and need to access buffer-local variables
  865. from that buffer within PREDICATE you must send them via
  866. PREDICATE-ARGS).
  867.  
  868. If more than two arguments are given to cookie-collect the remaining
  869. arguments will be passed to PREDICATE."
  870.  
  871.   (cookie-set-buffer buffer
  872.     (let ((tin (dll-nth cookies -2))
  873.       result)
  874.  
  875.       (while (not (eq tin cookie-header))
  876.  
  877.     (if (apply predicate
  878.            (cookie-tin-cookie (dll-element cookies tin))
  879.            predicate-args)
  880.         (setq result (cons (cookie-tin-cookie (dll-element cookies tin))
  881.                    result)))
  882.  
  883.     (setq tin (dll-previous cookies tin)))
  884.       result)))
  885.